package com.lagou.edu.systemContext;

import com.lagou.edu.annotation.*;
import com.lagou.edu.factory.ProxyFactory;
import com.lagou.edu.utils.ScanClassUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义IOC容器
 */
public class AnnotationApplicationContextUtils {

	private static Map<String, Object> map = new HashMap<>();

	static {
		try {
			//扫描包
			Map<String, Class<?>> resClassMap = null;
			resClassMap = ScanClassUtils.doScanClasses("com.lagou.edu");
			//获取注解类
			getAnnotationClassInfo(resClassMap);
			// 注入
			injectAnnotationClassInfo();
			// 事物注入
			transactionInject();

		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}catch (Exception e){
			e.printStackTrace();
		}
	}


	public static <T> T getContext(Class<?> classInfo) throws Exception {
		for (Object obj : map.values()) {
			if(classInfo.isInstance(obj)){
				return (T)obj;
			}else if(classInfo.isAssignableFrom(obj.getClass())){
				return (T)obj;
			}
		}
		return null;
	}


	public static void getAnnotationClassInfo(Map<String, Class<?>> classMap) throws Exception {
		for (Map.Entry<String, Class<?>> entry : classMap.entrySet()) {
			Class<?> clazzInfo = entry.getValue();
			if (clazzInfo.isAnnotationPresent(Componet.class)){
				Annotation componetAnnotation = clazzInfo.getAnnotation(Componet.class);
				String classRes = ((Componet) componetAnnotation).value();
				if(classRes.equals("")) {
					classRes = clazzInfo.getSimpleName();
				}
				map.put(classRes, clazzInfo.getDeclaredConstructor().newInstance());
			}else if (clazzInfo.isAnnotationPresent(Service.class)) {
				Annotation serviceAnnotation = clazzInfo.getAnnotation(Service.class);
				String classRes = ((Service) serviceAnnotation).value();
				if(classRes.equals("")) {
					classRes = clazzInfo.getSimpleName();
				}
				map.put(classRes, clazzInfo.getDeclaredConstructor().newInstance());
			}else if (clazzInfo.isAnnotationPresent(Respository.class)) {
				Annotation repositoryAnnotation = clazzInfo.getAnnotation(Respository.class);
				String classRes = ((Respository) repositoryAnnotation).value();
				if(classRes.equals("")) {
					classRes = clazzInfo.getSimpleName();
				}
				map.put(classRes, clazzInfo.getDeclaredConstructor().newInstance());
			}
		}
	}

	public static void injectAnnotationClassInfo() throws IllegalAccessException {
		for (Map.Entry<String,Object> entry : map.entrySet()) {
			Object object = entry.getValue();
			Class<?> clazz =object.getClass();
			Field[] fieldArr = clazz.getDeclaredFields();
			for(int i=0; i<fieldArr.length; i++) {
				Field field =fieldArr[i];
				Annotation autowiredAnnotation = field.getAnnotation(Autowired.class);
				if(autowiredAnnotation!=null) {
					Class<?> declaringClass = field.getType();
					for (Map.Entry<String,Object> entryInfo : map.entrySet()) {
						Object objectInfo = entryInfo.getValue();
						if(declaringClass.isInstance(objectInfo)) {
							field.setAccessible(true);
							field.set(object, objectInfo);
						}
					}
				}
			}
		}
	}

	public static void transactionInject() throws Exception {
		for (Map.Entry<String,Object> entry : map.entrySet()) {
			Object object = entry.getValue();
			Class<?> clazz =object.getClass();
			if (clazz.isAnnotationPresent(Transactional.class)) {
				String res = searchAnnotationInfo(clazz);
				if (res.equals("")){
					res = clazz.getSimpleName();
				}
				Class<?>[] interfaces = clazz.getInterfaces();
				ProxyFactory proxyFactory = getContext(ProxyFactory.class);
				if (interfaces.length == 0) {
					//cglib代理
					Object proxy = proxyFactory.getCglibProxy(object);
					map.put(res, proxy);
				} else {
					// jdk代理
					Object proxy = proxyFactory.getJdkProxy(object);
					map.put(res, proxy);
				}
			}
			Method[] methodArr = clazz.getDeclaredMethods();
			for(int i=0; i<methodArr.length; i++) {
				Method method =methodArr[i];
				Annotation tractionAnnotation = method.getAnnotation(Transactional.class);
				if (tractionAnnotation  != null ){
					String res = searchAnnotationInfo(clazz);
					if (res.equals("")){
						res = clazz.getSimpleName();
					}
					Class<?>[] interfaces =clazz.getInterfaces();
					Object obj = clazz.newInstance();
					ProxyFactory proxyFactory = getContext(ProxyFactory.class);
					if(interfaces.length == 0){
						//cglib代理
						Object proxy = proxyFactory.getCglibProxy(object);
						map.put(res,proxy);
					}else{
						// jdk代理
						Object proxy = proxyFactory.getJdkProxy(object);
						map.put(res,proxy);
					}
				}
			}
		}
	}

	private static String  searchAnnotationInfo(Class<?> clazzInfo){
		String classRes = "";
		if (clazzInfo.isAnnotationPresent(Componet.class)){
			Annotation componetAnnotation = clazzInfo.getAnnotation(Componet.class);
			classRes = ((Componet) componetAnnotation).value();
		}else if (clazzInfo.isAnnotationPresent(Service.class)) {
			Annotation serviceAnnotation = clazzInfo.getAnnotation(Service.class);
			classRes = ((Service) serviceAnnotation).value();
		}else if (clazzInfo.isAnnotationPresent(Respository.class)) {
			Annotation repositoryAnnotation = clazzInfo.getAnnotation(Respository.class);
			classRes = ((Respository) repositoryAnnotation).value();
		}
		return classRes;
	}



}
